<?php

namespace tools\jwt;

use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Parser;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\ValidationData;

class Token
{
    // 配置信息
    public static $_config = [
        'audience' => 'http://www.pyg.com',// 接收jwt的一方
        'issuer' => 'http://adminapi.pyg.com',// jwt签发者
        'id' => '3f2g57a92aa2',// 唯一标识
        'expire' => 3600 * 24,// 过期时间(秒)
        'sign' => 'pinyougou' // 签名秘钥
    ];

    // 生成token

    /**
     * 生成并获取用户的身份令牌
     *
     * 该方法用于根据用户ID生成一个JWT令牌，令牌包含了发行者、受众、唯一标识、发行时间、生效时间、过期时间等信息
     * 使用指定的签名算法确保令牌的安全性和有效性
     *
     * @param int $user_id 用户ID，用于标识令牌的拥有者
     * @return string 生成的用户身份令牌
     */
    public static function getToken($user_id): string
    {
        // 创建一个SHA-256签名算法实例
        $signer = new Sha256();
        // 获取当前时间戳
        $time = time();

        // 创建并配置JWT Builder实例，设置令牌的发行者、受众、唯一标识、发行时间、生效时间、过期时间等信息
        $token = (new Builder())->issuedBy(self::$_config['issuer']) // 设置令牌的发行者
        ->canOnlyBeUsedBy(self::$_config['audience']) // 设置令牌的受众
        ->identifiedBy(self::$_config['id'], true) // 设置令牌的唯一标识
        ->issuedAt($time) // 设置令牌的发行时间
        ->canOnlyBeUsedAfter($time - 1)// 设置令牌的生效时间
        ->expiresAt($time + self::$_config['expire']) // 设置令牌的过期时间
        // 在令牌中添加用户ID声明
        ->with('user_id', $user_id)
            // 使用SHA-256签名算法和密钥签名令牌
            ->sign($signer, self::$_config['sign'])
            ->getToken();

        // 返回生成的令牌字符串
        return (string)$token;
    }

    /**
     * 获取请求中的认证令牌
     *
     * 此函数旨在从HTTP请求的头部中提取认证令牌，特别适用于处理OAuth2标准中的Bearer令牌
     * 它检查请求头部是否存在，并移除令牌类型前缀，返回干净的令牌值
     *
     * @return mixed 如果未找到认证头部，则返回false；否则返回处理后的令牌字符串
     */
    public static function getRequestToken()
    {
        // 检查认证头部是否已提供，如果没有则返回false
        if (empty($_SERVER['HTTP_AUTHORIZATION'])) {
            return false;
        }

        // 获取认证头部的值
        $header = $_SERVER['HTTP_AUTHORIZATION'];

        // 定义预期的令牌类型，此处为'Bearer'
        $method = 'bearer';

        // 移除头部中的令牌类型前缀，返回实际的令牌值
        return trim(str_ireplace($method, '', $header));
    }

    /**
     * 获取用户ID
     *
     * 根据提供的token，验证并解析以获取用户ID如果token无效或验证失败，返回null
     *
     * @param string|null $token 可选的token字符串，如果未提供，将使用请求中的token
     * @return int|null 验证通过的用户ID，如果验证失败或token无效则返回null
     */
    public static function getUserId($token = null)
    {
        // 初始化用户ID为null
        $user_id = null;

        // 如果传入的token为空，则尝试从请求中获取token
        $token = empty($token) ? self::getRequestToken() : $token;

        // 如果token不为空，继续进行验证和解析
        if (!empty($token)) {
            //为了注销token 加以下if判断代码
            $delete_token = cache('delete_token') ?: [];
            if (in_array($token, $delete_token)) {
                //token已被删除（注销）
                return $user_id;
            }
            // 解析token
            $token = (new Parser())->parse((string)$token);

            // 初始化验证数据，并设置预期的发行者、受众和ID
            $data = new ValidationData();
            $data->setIssuer(self::$_config['issuer']);
            $data->setAudience(self::$_config['audience']);
            $data->setId(self::$_config['id']);

            // 验证token的有效性，如果验证失败，返回初始化的用户ID（null）
            if (!$token->validate($data)) {
                return $user_id;
            }

            // 初始化签名算法，并验证token的签名，如果验证失败，返回初始化的用户ID（null）
            $signer = new Sha256();
            if (!$token->verify($signer, self::$_config['sign'])) {
                return $user_id;
            }

            // 从验证通过的token中获取用户ID
            $user_id = $token->getClaim('user_id');
        }

        // 返回用户ID，可能为验证通过的用户ID或null
        return $user_id;
    }
}